import os
import traceback

import requests
import yaml
from requests.auth import HTTPBasicAuth
from string import Template

from common.log import Logger


"""
通过requests库来实现接口请求
1.安装requests库
2.导入requests库
3.通过requests实现get请求
4.由于jcmall商品进入需要用户身份鉴权，所以在请求中传入 auth=HTTPBasicAuth("deyunce", "828123")，实现用户身份的鉴权
5.通过res = requests.get()获取到的数据是一个Response类型的对象（Response类的实例）
6.获取取接口返回的数据调用Response对象下面的text即可，即：res.text->返回字符串；
7.获取接口返回的数据通过Response.json()把获取到的json转换成字符串；
8.发送一个带参数的get请求，需要把参数定义为一个字典对象，传入get(params=para_dict)
9.发送一个post请求，请求参数是json格式，requests.post(url,json=params_data); 
10.发送一个post请求，请求参数是form表达，requests.post(url,data=params_data);
11.获取接口响应状态码 Response.status_code;
12.通过参数headers可构造请求头，需要接收一个字典，一般用于传入接口必须的token；
13.获取服务器的响应头，Response.headers
14.jsonpath 提取接口响应, from jsonpath import jsonpath,  res = jsonpath(response, "$..token") -> 返回匹配到token的key的所有的value的列表
    如果没有匹配到值，则返回False;
"""
# 通过requests实现get请求
# url = "https://mall.deyunce.com/api/pc/index"
# res = requests.get(url, auth=HTTPBasicAuth("deyunce", "828123"))
# print(res)  # res是 Response对象
# print(type(res))
# # 获取接口的response数据(字符串）
# response = res.text
# print(response)
# print(type(response))
# # 获取接口的response数据(字典）
# response = res.json()  # 需要接口返回的是json格式的数据的时候才能用json()会把接口返回的json格式的数据转换成python的字典（反序列化）
# print(response)
# print(type(response))

# 练习：通过get请求获取url="https://mall.deyunce.com/api/goods_comment/lists?id=0&goods_id=595&page_size=10&page_no=1"的
# 字符串响应和json格式的响应
# 发送一个带参数的get请求 params参数接收一个字典
# url = "https://mall.deyunce.com/api/goods_comment/lists"
# param_data = {"id":0, "goods_id":595, "page_size":10, "page_no":1}
# res = requests.get(url, params=param_data, auth=HTTPBasicAuth("deyunce", "828123"))
# # 获取接口的json格式的响应
# response = res.json()
# print(response)
# # 获取接口的纯文本格式响应
# response = res.text
# print(response)

# 接口说明：域名：https://mall.deyunce.com,  api: /api/goods/getGoodsDetail, 请求方式method:GET, 请求参数：id=752
# 练习：通过requests请求上述接口，获取json格式的响应

# 通过requests发送post请求,请求参数是json,此时需要把参数传给post方法中的json参数
# url = "https://mall.deyunce.com/api/account/login"
# params_data = {"account": "15200000001", "password": "111111.", "client": 5}
# res = requests.post(url, json=params_data, auth=HTTPBasicAuth("deyunce", "828123"))
# # 获取json格式的响应
# response = res.json()  # 把接口返回的json格式的响应数据转换（反序列化）成字典
# print(response)
# login_res = response.get("msg")
# print(login_res)
# # 获取接口的响应码
# status_code = res.status_code
# print(status_code)

# 练习：抓取登录接口，对登录接口发起登录请求，获取接口的响应状态码，响应体中的 msg,和token
# 通过requests发送post请求，请求参是就表单形式,此时需要把参数传给post方法中的data参数
# url = "https://mall.deyunce.com/api/account/login"
# params_data = {"account": "15200000001", "password": "111111.", "client": 5}
# res = requests.post(url, data=params_data, auth=HTTPBasicAuth("deyunce", "828123"))

# 通过get请求实现调用购物车接口,通过构造请求头header实现token传入
# url = "https://mall.deyunce.com/api/cart/lists"
# header = {"Token": "31f8e96283be93ca39ec6a38457a3a83"}  # header的构造，是一个字典
# res = requests.get(url, headers=header, auth=HTTPBasicAuth("deyunce", "828123"))
# print(res.status_code)
# print(res.text)

# 练习：构造调用商品搜索接口，实现搜索“文具”，并打印接口响应
# url = "https://mall.deyunce.com/api/pc/goodsList"
# param_data = {"page_size":20, "name":"文具"}
# header = {"Token":"ae22e01b18f9c92db047142488784098"}
# res = requests.get(url, params=param_data, headers=header,  auth=HTTPBasicAuth("deyunce", "828123"))
# print(res.text)
# print(res.json())
# #获取响应体中的header
# response_header = res.headers
# print(response_header)
# print(response_header.get('Content-Type'))
# print(type(response_header.get('Content-Type')))
# print(type(response_header))
logging = Logger(__name__).get_logger()
class BaseApi:

    """
    接口自动化实现目的：脱离前端验证后端的业务逻辑，从业务场景的维度出发，来构造测试用例
    把涉及的单个业务接口以最小的粒度封装成单个方法，在测试用例中通过接口方法的组合调用来实现业务流；
    封装一个BaseApi的接口基类，在
    """


    # 对requests进行二次封装
    def request_api(self, method, api, params=None, header=None, data=None, json=None, auth=HTTPBasicAuth("deyunce", "828123")):
        host = "https://mall.deyunce.com"
        api_url = host + api
        response_dict = {}  # 定义一个空的字典用于存放接口的响应码，和接口的响应数据
        if method in ["get","GET"]:
            try:
                res = requests.get(url=api_url, params=params, headers=header, auth=auth)
                # 获取响应头
                response_header = res.headers
                response_type = response_header.get('Content-Type')
                if response_type == "application/json; charset=utf-8":
                    response = res.json()
                else:
                    response = res.text
                # 获取响应码
                status_code = res.status_code
                response_dict["status_code"] = status_code
                response_dict["response"] = response
                return response_dict  # {"statuc_code": 200, "response":{"code":1, "msg":"登录成功”}}
            except Exception as e:
                error_info = traceback.format_exc()
                logging.error(f"接口::{api}::请求出错::{e},错误详情:{error_info}")
        elif method in ["post", "POST"]:
            try:
                res = requests.post(url=api_url, params=params, data=data, json=json, headers=header,auth=HTTPBasicAuth("deyunce", "828123"))
                # 获取响应头
                response_header = res.headers
                response_type = response_header.get('Content-Type')
                if response_type == "application/json; charset=utf-8":
                    response = res.json()
                else:
                    response = res.text
                # 获取响应码
                status_code = res.status_code
                response_dict["status_code"] = status_code
                response_dict["response"] = response
                return response_dict
            except Exception as e:
                error_info = traceback.format_exc()
                logging.error(f"接口::{api}::请求出错::{e},错误详情:{error_info}")


    def run_requests(self, yaml_path, api_name, **kwargs):
        """
        读取yaml文件中的数据，把yaml中的数据传入request_api实例方法中，实现对接口的请求，并把请求结果和响应码返回出去
        :return:
        """
        with open(yaml_path, "r", encoding="UTF-8") as f:
            yaml_datas = yaml.safe_load(f)
        dic_api_datas = yaml_datas.get(api_name)
        # 在不丢失原数据类型描述的基础上转换成字符串（yaml语法的字符串）
        str_api_datas = yaml.dump(dic_api_datas)
        print(f"获取到的kwargs是：{kwargs}")
        if kwargs:
            for k in kwargs.keys():
                if isinstance(kwargs[k], str):
                    kwargs[k] = f"'{kwargs[k]}'"
            str_api_datas = Template(str_api_datas).substitute(kwargs) # 此时str_api_datas 是字符串类型，kwargs是待替换的键值对
        dic_api_datas = yaml.safe_load(str_api_datas)
        logging.info(f"读取YAML文件::{yaml_path}::请求的接口名称::{api_name}::传入接口参数::{kwargs}::接口请求参数api_datas::{dic_api_datas}")
        res = self.request_api(
                               method=dic_api_datas.get("method"),
                               api=dic_api_datas.get("api"),
                               params=dic_api_datas.get("params"),
                               data=dic_api_datas.get("data"),
                               json=dic_api_datas.get("json"),
                               header=dic_api_datas.get("headers")
                               )
        logging.info(f"接口::{dic_api_datas.get('api')}::响应数据为::{res}")
        return res

    @staticmethod
    def get_path(relative_path):# 项目的相对路径
        '''
        文件的路径拼接，返回一个绝对路径
        :param relative_path: 文件的相对路径
        :return: 文件的绝对路径
        '''
        file_path = os.path.abspath(__file__)  # 获取当前py的绝对路径
        dir_path = os.path.dirname(file_path)
        far_path = os.path.dirname(dir_path)
        path = far_path + relative_path
        return path




if __name__ == '__main__':
    base_api = BaseApi()
    # params_data = {"id":752}
    # header = {"Token":'ae22e01b18f9c92db047142488784098'}
    # res = base_api.request_api(method="get", api="/api/goods/getGoodsDetail", params=params_data, header=header)
    # print(res)

    # params_data = {"account":"15200000001","password":"111111.","client":5}
    # res = base_api.request_api(method="post", api="/api/account/login", json=params_data)
    # print(res)
    base = BaseApi()
    yaml_path = "../api_datas/loginRegisterApis.yaml"
    base.run_requests(yaml_path, "register_api", tel="15200000001", pwd="111111.")


'''
接口场景式自动化：更多关注后端逻辑，脱离前端验证后端的业务逻辑（必备的要素：需要测试人员对自己所负责业务的后端有充分了解）
'''



